#include "./options.h"
#include "../sys/string.h"

mds_option *mds_option_create( const char *name, const char *value )
{
	mds_option *option = NULL;
	
	assert( name  != NULL );
	assert( value != NULL );
	
	if( NULL == ( option = MDS_CALLOC_AS(mds_option,1) ) )
	{
		return NULL;
	}
	
	if( NULL == ( option->name = mds_string_copy( name ) ) )
	{
		MDS_FREE_AS(mds_option, option, 1 );
		return NULL;
	}
	
	if( NULL == (option->value = mds_string_copy( value ) ) )
	{
		mds_string_delete( option->name );
		MDS_FREE_AS(mds_option, option, 1 );
		return NULL;
	}
	
	
	return option;
}

void mds_option_delete( mds_option *option )
{
	assert( option );
	assert( option->name );
	assert( option->value != NULL );
	assert( NULL == option->prev );
	assert( NULL == option->next );
	mds_string_delete( option->value );
	mds_string_delete( option->name  );
	MDS_FREE_AS(mds_option, option, 1 );
}


void mds_options_init( mds_options *options )
{
	assert( options );
	memset( options, 0, sizeof(mds_options) );
}

void mds_options_quit( mds_options *options )
{
	assert( options );
	while( options->size ) mds_option_delete( mds_list_pop_back(mds_option,options) );
}

int  mds_options_parse( mds_options *options, int argc, char **argv ) 
{
	int i = 0;
	assert( options != NULL );
	assert( argc > 0 );
	assert( argv != NULL );
	
	while( ++i < argc )
	{
		const char *name = argv[i];
		if( NULL == name || strlen(name) < 2 || name[0] != '-' )
		{
			/** invalid name **/
			errno = EINVAL;
			return -1;
		}
		++name;
		if( ++i >= argc )
		{
			/** missing value **/
			errno = EINVAL;
			return -1;
		}
		else {
			const char  *value = argv[i];
			mds_option  *option = mds_option_create( name, value);
			if( !option )
				return -1;
			fprintf( stderr, "[mds] found option '%s'='%s'\n",  option->name, option->value );
			mds_list_push_back( options, option ); 
		}
		
	}
	
	
	
	return 0;
}


void mds_options_help( FILE *fp, const mds_option_hook *reg, const size_t num )
{
	register size_t i;
	assert( fp );
	assert( reg  );
	fprintf( fp, "\tOPTIONS:\n");
	for( i=0; i < num; ++i, ++reg )
	{
		fprintf( fp, "\t\t(%s) -%-16s: %s\n", reg->forced ? "FORCED" : "NORMAL", reg->name, reg->info );
	}
}

void mds_options_display( const mds_options *options, FILE *fp )
{
	const mds_option *option;
	assert( options );
	assert( fp );
	
	fprintf( fp, "#options=%u\n", (unsigned) (options->size) );
	for( option = options->head; option != NULL; option = option->next )
	{
		fprintf( fp, "\t'%s'='%s'\n", option->name, option->value );
	}
}



#include <ctype.h>

int mds_options_load( mds_options *options, const char *filename, const char *after)
{
	static const char endl[] = "\r\n";
	
	int   res  = 0;
	char *data = NULL;
	char *line = NULL;
	char *ibrk = NULL;
	mds_options file_options = MDS_OPTIONS_INIT;
	mds_option *target       = NULL;
	
	assert( options );
	assert( filename );
	
	/************************/
	/** load file content **/
	/**********************/
	if( NULL == ( data = mds_string_read( filename ) ) )
	{
		res = -1; 
		goto END;
	}
	
	/** parse lines **/
	for( line = mds_strtok_r(data, endl, &ibrk ); line; line = mds_strtok_r( NULL, endl, &ibrk) )
	{
		size_t len = strlen( line );
		
		/** skip blanks **/
		while( len > 0 && isspace( line[0] ) ) { --len; ++line; }
		/*fprintf( stderr, "[%s]\n", line );*/
		
		/** check not an empty/comment line **/
		if( len <= 0 || line[0] == '#' ) 
			continue;
		else 
		{
			/** find option name **/
			char   *name  = line;
			char   *value = NULL;
			size_t  vsize = 0;
			size_t  i     = 0;
			while( i < len )
			{
				if( line[i] == '=' )
					break;
				++i;
			}
			
			/** no option ! */
			if( i >= len )
			{
				errno = EINVAL;
				res   = -1;
				goto END;
			}
			
			/** remove options name blanks and 
			 keep value position
			 */
			line[i] = ' ';
			value = &line[i+1];
			while( i>0 && isspace( line[i] ) )
			{
				line[i--] = 0;
			}
			
			/** clean up value **/
			vsize = strlen( value );
			while( vsize > 0 && isspace( value[0] ) )
			{
				--vsize; ++value;
			}
			while( vsize > 0 && isspace( value[vsize-1] ) )
			{
				value[ --vsize ] = 0;
			}
			
			/* fprintf( stderr, "name='%s' | value='%s'\n", name, value );
			 */
			
			/** create the option and append it ! */
			{
				mds_option *option = mds_option_create(name, value);
				if( !option )
				{
					res = -1;
					goto END;
				}
				mds_list_push_back( &file_options, option );
				fprintf( stderr, "[mds] found option '%s'='%s' (in file)\n", name,  value );
			}
			
		}
		
	}
	
	
	/** Look up for the target option **/
	if( after )
	{
		target = options->head;
		while( target && strcmp( after, target->name) !=0 ) target = target->next;
		if( target )
		{
			mds_options swap = MDS_OPTIONS_INIT;

			/** move options after target in swap **/
			while( options->tail != target ) mds_list_push_front( &swap, mds_list_pop_back(mds_options,options) );
			
			/** move swap at the end of file_options **/
			while( swap.size > 0 ) mds_list_push_back( &file_options, mds_list_pop_front(mds_option,&swap) );

		}
	}
		
	
	/** merge the file_options with options **/
	while( file_options.size ) mds_list_push_back( options, mds_list_pop_front(mds_options,&file_options ) );
	

END:
	if(data) mds_string_delete(data);
	mds_options_quit( &file_options );
	return res;
}

